/*******************************************************************************
 * Copyright (c) 2008, 2009 Tapestry IDE Developers
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Tapestry IDE Developers - initial API and implementation
 *******************************************************************************/
package com.googlecode.tapestry.ide.internal.core.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.googlecode.tapestry.ide.core.TapestryCoreUtils;
import com.googlecode.tapestry.ide.core.model.AbstractModelElement;
import com.googlecode.tapestry.ide.core.model.IModelElement;
import com.googlecode.tapestry.ide.core.model.ITapestryContainer;
import com.googlecode.tapestry.ide.core.model.ITapestryItem;

// TODO: make thread-safe
/**
 * The Class TapestryContainer.
 */
public class TapestryContainer<I extends ITapestryItem> extends
		AbstractModelElement implements ITapestryContainer<I> {

	/** The rwl. */
	private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

	/** The r. */
	private final Lock r = rwl.readLock();

	/** The w. */
	private final Lock w = rwl.writeLock();

	/** The items. */
	private Map<String, I> items = new HashMap<String, I>();

	/**
	 * Instantiates a new tapestry container.
	 * 
	 * @param parent
	 *            the parent
	 * @param name
	 *            the name
	 */
	public TapestryContainer(IModelElement parent, String name) {
		super(parent, name);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @seecom.googlecode.tapestry.ide.core.model.AbstractModelElement#
	 * getElementChildren()
	 */
	@Override
	public IModelElement[] getElementChildren() {
		try {
			r.lock();
			return (IModelElement[]) items.values().toArray(
					new IModelElement[items.size()]);
		} finally {
			r.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.googlecode.tapestry.ide.core.model.ITapestryContainer#getItem(java
	 * .lang.String)
	 */
	@Override
	public I getItem(String name) {
		try {
			r.lock();
			return items.get(name);
		} finally {
			r.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.googlecode.tapestry.ide.core.model.ITapestryContainer#addItem(com
	 * .googlecode.tapestry.ide.core.model.ITapestryItem)
	 */
	public void addItem(I item) {
		try {
			w.lock();
			items.put(item.getElementName(), item);
		} finally {
			w.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.googlecode.tapestry.ide.core.model.ITapestryContainer#removeItem(
	 * com.googlecode.tapestry.ide.core.model.ITapestryItem)
	 */
	public I removeItem(I item) {
		return removeItem(item.getElementName());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.googlecode.tapestry.ide.core.model.ITapestryContainer#removeItem(
	 * java.lang.String)
	 */
	@Override
	public I removeItem(String name) {
		try {
			w.lock();
			return items.remove(name);
		} finally {
			w.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.googlecode.tapestry.ide.core.model.ITapestryContainer#getItems()
	 */
	@Override
	public List<I> getItems() {
		try {
			r.lock();
			List<I> list = new ArrayList<I>();
			list.addAll(items.values());
			return list;
		} finally {
			r.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (this.getElementParent() == null) {
			return super.equals(other);
		}
		if (!(other instanceof ITapestryContainer<?>)) {
			return false;
		}
		AbstractModelElement that = (AbstractModelElement) other;
		return getElementName().equals(that.getElementName())
				&& getElementParent().equals(that.getElementParent());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.googlecode.tapestry.ide.core.model.AbstractModelElement#hashCode()
	 */
	@Override
	public int hashCode() {
		if (getElementParent() == null) {
			return super.hashCode();
		}
		return TapestryCoreUtils.combineHashCodes(getElementName().hashCode(),
				getElementParent().hashCode());
	}

}
